package org.chronologger.poppaea.client.overlap;

import java.util.Iterator;
import java.util.PriorityQueue;

// TODO: it should be possible to do all that more efficiently
public class OverlapGroup implements Range, Iterable<Range> {

	// first first
	private PriorityQueue<Range> byFrom = new PriorityQueue<Range>(11,
			Range.byFrom);

	// last first
	private PriorityQueue<Range> byTo = new PriorityQueue<Range>(11,
			Range.byToInverse);

	public void addRange(Range range) {
		byFrom.add(range);
		byTo.add(range);
	}

	public void removeRange(Range range) {
		byFrom.remove(range);
		byTo.remove(range);
	}

	public int getFrom() {
		if (byFrom.isEmpty())
			return Integer.MAX_VALUE;
		else
			return byFrom.peek().getFrom();
	}

	public int getTo() {
		if (byTo.isEmpty())
			return Integer.MIN_VALUE;
		else
			return byTo.peek().getTo();
	}

	public boolean isEmpty() {
		return byFrom.isEmpty();
	}

	/**
	 * makes the member of this group join another group. This group will be
	 * empty after this call returns.
	 * 
	 * @param otherGroup
	 */
	public void join(OverlapGroup otherGroup) {
		for (Range range : byFrom) {
			otherGroup.addRange(range);
		}
		byFrom.clear();
		byTo.clear();
	}

	/**
	 * Checks whether this group is connected.
	 * 
	 * @return
	 */
	public boolean isConnected() {
		if (byFrom.size() < 2)
			return true;

		PriorityQueue<Range> byf = new PriorityQueue<Range>(byFrom);

		Range next = byf.remove();
		int maxTo = Integer.MIN_VALUE;
		while (!byf.isEmpty()) {
			Range crnt = next;
			next = byf.remove();

			if (maxTo < crnt.getTo())
				maxTo = crnt.getTo();

			if (next.getFrom() > maxTo)
				return false;
		}

		return true;
	}

	/**
	 * removes all members of this group that don't belong to the first (sorted
	 * by from) connected part. If the group whole was connected, it will return
	 * null.
	 * 
	 * @return
	 */
	public OverlapGroup split() {
		if (byFrom.size() < 2)
			return null;

		// a copy of the byFrom priority queue
		PriorityQueue<Range> rangesByFrom = new PriorityQueue<Range>(byFrom);
		// if a split occurs, this contains the remaining entries of the byFrom
		// priority queue
		PriorityQueue<Range> newByFrom = new PriorityQueue<Range>(11,
				Range.byFrom);

		Range next = rangesByFrom.remove();
		// the maximum value of any range of this group
		int maxTo = Integer.MIN_VALUE;
		while (!rangesByFrom.isEmpty()) {
			Range crnt = next;
			newByFrom.add(crnt);
			next = rangesByFrom.remove();

			if (maxTo < crnt.getTo())
				maxTo = crnt.getTo();

			// if the next range's start point is bigger than the current
			// maximum value, it's not connected.
			if (next.getFrom() >= maxTo) {
				// move the rest of the rangesByFrom priority queue to a new
				// group.
				OverlapGroup result = new OverlapGroup();
				result.byFrom = rangesByFrom;
				result.byFrom.add(next);
				result.byTo.addAll(result.byFrom);

				// update this group
				byFrom = newByFrom;
				byTo = new PriorityQueue<Range>(11, Range.byToInverse);
				byTo.addAll(byFrom);

				return result;
			}
		}

		return null;
	}

	public Iterator<Range> iterator() {
		return byFrom.iterator();
	}

	PriorityQueue<Range> getFromPriorityQueue() {
		return byFrom;
	}

}